package com.itouchtv.yang.core.driver;

import com.itouchtv.yang.core.tools.ConfigManager;
import com.itouchtv.yang.core.tools.Log;
import com.itouchtv.yang.library.element.PageElement;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;
import org.hamcrest.Matchers;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.testng.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

/**
 * driver基类，用于选择浏览器，以及浏览器的各种操作方法
 */
public class BaseDriver {

    private EventFiringWebDriver webDriver;
    private String driverName;
    private Actions actions;
    private TouchAction touchAction;

    public BaseDriver() {
        String remote = ConfigManager.getProperty("UseRemote","false");
        driverName = ConfigManager.getProperty("Browser", "").toUpperCase();
        if (remote.equals("true")){
            webDriver = DriverSelect.selectRemoteDriver(driverName);
        }else {
            webDriver = DriverSelect.selectDriver(driverName);
        }
        Objects.requireNonNull(webDriver,"webDriver：null");
        actions = new Actions(webDriver);
        if (webDriver.getWrappedDriver() instanceof AppiumDriver){
            touchAction = new TouchAction((AppiumDriver)(webDriver.getWrappedDriver()));
        }
        driverPropertInit();
    }

    public BaseDriver(String driver){
        String remote = ConfigManager.getProperty("UseRemote","false");
        driverName = driver.toUpperCase();
        if (remote.equals("true")){
            webDriver = DriverSelect.selectRemoteDriver(driver);
        }else {
            webDriver = DriverSelect.selectDriver(driver);
        }
        Objects.requireNonNull(webDriver,"webDriver：null");
        actions = new Actions(webDriver);
        if (webDriver.getWrappedDriver() instanceof AppiumDriver){
            touchAction = new TouchAction((AppiumDriver)(webDriver.getWrappedDriver()));
        }
        driverPropertInit();
    }

    public String getDriverName(){
        return driverName;
    }
    public WebDriver getWebDriver() {
        return webDriver;
    }

    /**
     * 配置浏览器的大小以及等待时间
     */
    private void driverPropertInit() {
        Objects.requireNonNull(webDriver,"webDriver:null");
        String os = System.getProperty("os.name");

        if (!os.toUpperCase().contains("WINDOWS") && webDriver.getWrappedDriver() instanceof ChromeDriver){
            webDriver.manage().window().setPosition(new Point(0,0));
            webDriver.manage().window().setSize(new Dimension(1920,1080));
        }else if (!(webDriver.getWrappedDriver() instanceof AppiumDriver)){
            webDriver.manage().window().maximize();
            webDriver.manage().timeouts().pageLoadTimeout(8, TimeUnit.SECONDS);
        }
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }

    public void openUrl(String url) {
        webDriver.get(url);
    }

    public PageElement findElementNoFrame(By by){
        PageElement pageElement=null;
        WebElement webElement=null;
        try {
            webElement = webDriver.findElement(by);
        } catch (NoSuchElementException e) {
            return null;
        }
        if (Objects.nonNull(webElement)) {
            pageElement = new PageElement();
            pageElement.setWebElement(webElement);
            pageElement.setBy(by);
            return pageElement;
        } else{
            return null;
        }
    }

    /**
     * 查找元素
     * @param by 元素BY
     * @return pageElement
     */
    public PageElement findElement(By by) {
        PageElement pageElement=null;
        WebElement webElement=null;
        try {
            webElement = webDriver.findElement(by);
        } catch (NoSuchElementException e) {
            Log.logger.info("没有找到元素，尝试在iframe和frame中查找");
            pageElement = findElementInFrame(by, "iframe");
            if (Objects.isNull(pageElement)|| Objects.isNull(pageElement.getWebElement())) {
                pageElement = findElementInFrame(by, "frame");
            }
        }
        if (Objects.nonNull(webElement)) {
            pageElement = new PageElement();
            pageElement.setWebElement(webElement);
            pageElement.setBy(by);
            return pageElement;
        } else if (Objects.nonNull(pageElement)&& Objects.nonNull(pageElement.getWebElement())) {
            return pageElement;
        } else{
            return null;
        }
    }

    /**
     * 查找位于frame中的元素
     * @param by 用于定位元素，包括定位方式和定位字符串
     * @param frameName frame元素的tageName,有frame和iframe
     * @return PageElement
     */
    private PageElement findElementInFrame(By by, String frameName) {
        PageElement pageElement;
        WebElement webElement;
        webDriver.manage().timeouts().implicitlyWait(0,TimeUnit.SECONDS);
        for (WebElement element : webDriver.findElements(By.tagName(frameName))) {
            webDriver.switchTo().frame(element);
            try {
                webElement = webDriver.findElement(by);
            } catch (NoSuchElementException e) {
                webDriver.switchTo().defaultContent();
                continue;
            }
            webDriver.switchTo().defaultContent();
            if (Objects.nonNull(webElement)) {
                pageElement = new PageElement();
                pageElement.setBy(by);
                pageElement.setWebElement(webElement);
                pageElement.setFrame(element);

                webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);
                return pageElement;
            }
        }
        webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);
        return null;
    }

    public List<PageElement> findElements(By by) {
        List<WebElement> list = webDriver.findElements(by);
        List<PageElement> listP = new ArrayList<>();
        for (WebElement webElement:list){
            PageElement pageElement =new PageElement();
            pageElement.setWebElement(webElement);
            listP.add(pageElement);
        }
        return listP;
    }

    public void sendKey(PageElement pageElement, CharSequence s, Boolean clear) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            if (clear) {
                pageElement.clear();
            }
            pageElement.sendKeys(s);
            switchToDefault();
        } else {
            scrollElement(pageElement);
            if (clear) {
                pageElement.clear();
            }
            pageElement.sendKeys(s);
        }
        sleep(300);
    }

    public void mouseClick(PageElement pageElement) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            pageElement.mouseClick(actions);
            switchToDefault();
        } else {
            scrollElement(pageElement);
            pageElement.mouseClick(actions);
        }
        sleep(2000);
    }

    public void refresh(){
        webDriver.navigate().refresh();
    }

    public void mouseClick(PageElement pageElement,int xOffset,int yOffset) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            pageElement.mouseClick(actions,xOffset,yOffset);
            switchToDefault();
        } else {
            scrollElement(pageElement);
            pageElement.mouseClick(actions,xOffset,yOffset);
        }
        sleep(2000);
    }

    public void mouseMoveElement(PageElement pageElement) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            pageElement.mouseMoveTo(actions);
            switchToDefault();
        } else {
            scrollElement(pageElement);
            pageElement.mouseMoveTo(actions);
        }
        sleep(2000);
    }

    public void onClick(PageElement pageElement){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            pageElement.click();
            switchToDefault();
        } else {
            scrollElement(pageElement);
            pageElement.click();
        }
        sleep(500);
    }

    public String getElementText(PageElement pageElement){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            scrollElement(pageElement);
            String s =  pageElement.getText();
            switchToDefault();
            return s;
        } else {
            scrollElement(pageElement);
            return pageElement.getText();
        }
    }

    public void touchPress(PageElement pageElement){
        Objects.requireNonNull(touchAction,"非android、ios不能使用此方法");
        pageElement.touchPress(touchAction);
    }

    public void clickAlertAccept(){
        webDriver.switchTo().alert().accept();
        webDriver.switchTo().defaultContent();
    }

    public void clickAlertDismiss(){
        webDriver.switchTo().alert().dismiss();
        webDriver.switchTo().defaultContent();
    }

    public void checkAlertText(String text){
        Assert.assertEquals(webDriver.switchTo().alert().getText(),text);
        webDriver.switchTo().defaultContent();
    }

    public void sendAndClickAlert(String keys){
        webDriver.switchTo().alert().sendKeys(keys);
        sleep(1000);
        webDriver.switchTo().alert().accept();
        webDriver.switchTo().defaultContent();
    }


    /**
     * 切换窗口
     * @param index 窗口位置
     */
    public void switchtoWindow(int index){
        Object[] handles = webDriver.getWindowHandles().toArray();
        webDriver.switchTo().window(handles[index].toString());
    }

    /**
     * 返回上一步
     */
    public void back(){
        webDriver.navigate().back();
        sleep(3000);
    }

    /**
     * 关闭当前窗口
     */
    public void closeWindow(){
        Log.logger.info("关闭窗口："+webDriver.getWindowHandle());
        webDriver.close();
    }

    /**
     * 退出浏览器
     */
    public void closeBrowser() {
        Log.logger.info("退出浏览器");
        if (Objects.nonNull(webDriver)){
            webDriver.quit();
            CommDriver.driver.set(null);
        }
    }

    /**
     * select元素，选中其中一个选项
     * @param pageElement 元素对象
     * @param index 选项位置
     */
    public void select(PageElement pageElement,int index){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            pageElement.select(index);
            switchToDefault();
        } else {
            pageElement.select(index);
        }
    }

    public void select(PageElement pageElement,String value){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            pageElement.select(value);
            switchToDefault();
        } else {
            pageElement.select(value);
        }
    }

    /**
     * 检查元素text文本
     * @param pageElement 元素对象
     * @param text 文本
     */
    public void checkText(PageElement pageElement, String text) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            assertThat(pageElement.getText(), equalTo(text));
            switchToDefault();
        } else {
            assertThat(pageElement.getText(), equalTo(text));
        }
    }

    /**
     * 检查当前页面的url
     * @param url url
     */
    public void checkUrl(String url) {
        sleep(3000);
        Assert.assertEquals(webDriver.getCurrentUrl(), url);
    }

    /**
     * 检查页面title
     * @param title 标题
     */
    public void checkTitle(String title) {
        sleep(3000);
        Assert.assertEquals(webDriver.getTitle(), title);
    }

    /**
     * 检查页面是否包含某个字符串
     * @param text 字符串
     */
    public void checkPageString(String text) {
        assertThat(webDriver.getPageSource(), containsString(text));
    }

    /**
     * 检查元素是否存在
     * @param pageElements 元素列表
     */
    public void checkExistWebElement(List<PageElement> pageElements,Boolean isThere) {
        if (isThere){
            if (!(pageElements.size()>0)) assertThat("检查元素不存在，与预期不符",false);
        }else {
            if (!Objects.equals(pageElements.size(),0)) assertThat("检查到元素存在，与预期不符",false);
        }
    }

    public Boolean isExistWebElement(PageElement pageElement){
        if (Objects.isNull(findElement(pageElement.getBy()))){
            return false;
        }else return true;
    }

    /**
     * 检查元素属性
     * @param pageElement 元素对象
     * @param attributeName 属性名
     * @param value 属性值
     */
    public void checkAttribute(PageElement pageElement, String attributeName, String value) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            assertThat(pageElement.getAttribute(attributeName), equalTo(value));
            switchToDefault();
        } else {
            assertThat(pageElement.getAttribute(attributeName), equalTo(value));
        }
    }

    /**
     * 设置元素属性
     * @param pageElement 元素名称
     * @param attributeName 属性名称
     * @param value 属性值
     */
    public void setAttribute(PageElement pageElement,String attributeName,String value){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            runJs("arguments[0]."+attributeName+" = '"+value+"';",pageElement.getWebElement());
            switchToDefault();
        } else {
            runJs("arguments[0]."+attributeName+" = '"+value+"';",pageElement.getWebElement());
        }
        sleep(1000);
    }

    /**
     * 移除元素属性
     * @param pageElement 元素名
     * @param attributeName 属性名
     */
    public void removeAttribute(PageElement pageElement,String attributeName){
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            runJs("arguments[0].removeAttribute('"+attributeName+"');",pageElement.getWebElement());
            switchToDefault();
        } else {
            runJs("arguments[0].removeAttribute('"+attributeName+"');",pageElement.getWebElement());
        }
        sleep(1000);
    }

    /**
     * 检测元素是否可见
     * @param pageElement 元素
     * @param displayed 是否可见
     */
    public void checkDisplayed(PageElement pageElement,Boolean displayed) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            assertThat(displayed, equalTo(pageElement.isDisplayed()));
            switchToDefault();
        } else {
            assertThat(displayed, equalTo(pageElement.isDisplayed()));
        }
    }

    /**
     * 检测元素是否可点击
     * @param pageElement 元素
     * @param enabled 是否可点击
     */
    public void checkEnabled(PageElement pageElement,Boolean enabled) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            assertThat(enabled, equalTo(pageElement.isEnabled()));
            switchToDefault();
        } else {
            assertThat(enabled, equalTo(pageElement.isEnabled()));
        }
    }

    /**
     * 检测元素是否被选中
     * @param pageElement 元素
     * @param selected 是否被选中
     */
    public void checkSelected(PageElement pageElement,Boolean selected) {
        if (Objects.nonNull(pageElement.getFrame())) {
            switchToFrame(pageElement.getFrame());
            assertThat(selected, equalTo(pageElement.isSelected()));
            switchToDefault();
        } else {
            assertThat(selected, equalTo(pageElement.isSelected()));
        }
    }

    public void sleep(long millis) {
        try {
            Log.logger.info("等待:"+millis+"ms");
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void switchToFrame(WebElement webElement) {
        webDriver.switchTo().frame(webElement);
    }

    public void switchToDefault() {
        webDriver.switchTo().defaultContent();
    }

    /**
     * 执行JS
     * @param js js代码
     * @param args 可选参数
     */
    private void runJs(String js, Object... args) {
        ((JavascriptExecutor) webDriver).executeScript(js, args);
    }

    /**
     * 窗口滚动
     * @param pageElement 根据某个元素的坐标来决定滚动的距离
     */
    private void scrollElement(PageElement pageElement){
        WebElement webElement = pageElement.getWebElement();
        int webElementX = webElement.getLocation().x;
        int webElementY = webElement.getLocation().y;
        int windowX = webDriver.manage().window().getSize().width;
        int windowY = webDriver.manage().window().getSize().height;
        int offsetX = webElementX-windowX/2;
        int offsetY = webElementY-windowY/2;
        if (offsetX>windowX/3||offsetY>windowY/3){
            Log.logger.info("窗口偏移："+offsetX+","+offsetY);
            runJs("window.scrollBy("+offsetY+","+offsetY+");");
            sleep(1000);
        }
//        if(webElementX<0||webElementX>windowX||webElementY<200||webElementY>windowY){
//            Log.logger.info("窗口滚动");
//            runJs("window.scrollTo("+windowX/2+Math.abs(webElementX)+","+windowY/2+Math.abs(webElementY)+")");
//        }
//        runJs("arguments[0].scrollIntoView(true);", pageElement.getWebElement());
    }
}
